package de.pharmacontrol.psa.beans;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ejb.EJB;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import com.sap.conn.jco.JCoException;
import com.sap.conn.jco.ext.DestinationDataProvider;
import com.sap.conn.jco.ext.Environment;
import com.sap.conn.jco.ext.ServerDataProvider;
import com.sap.conn.jco.rt.JCoRuntime;
import com.sap.conn.jco.rt.JCoRuntimeFactory;
import com.sap.conn.jco.server.DefaultServerHandlerFactory;
import com.sap.conn.jco.server.JCoServer;
import com.sap.conn.jco.server.JCoServerFactory;
import com.sap.conn.jco.server.JCoServerState;
import com.sap.conn.jco.server.JCoServerStateChangedListener;
import com.sap.conn.jco.util.AboutDialog;

import de.pharmacontrol.pilot.entities.Line;
import de.pharmacontrol.psa.beans.interfaces.JCoBean;
import de.pharmacontrol.psa.beans.interfaces.LineService;
import de.pharmacontrol.psa.beans.interfaces.LoggerService;
import de.pharmacontrol.rfcserver.PceDestinationDataProvider;
import de.pharmacontrol.rfcserver.PceRfcErrorListener;
import de.pharmacontrol.rfcserver.PceServerDataProvider;
import de.pharmacontrol.rfcserver.handler.CreateOrderFunctionHandler;
import de.pharmacontrol.rfcserver.handler.DeleteOrderFunctionHandler;
import de.pharmacontrol.rfcserver.handler.GetOrderResultsFunctionHandler;
import de.pharmacontrol.rfcserver.handler.GetOrderStatusFunctionHandler;
import de.pharmacontrol.rfcserver.handler.PceTIDHandler;

/**
 * Session Bean implementation class JConBean
 */
@Startup
@Singleton
public class JCoStartupBean implements JCoBean, JCoServerStateChangedListener {

	@PersistenceContext(unitName = "pilot")
	protected EntityManager em;

	@EJB
	LineService lineService;

	@EJB
	LoggerService logger;

	@EJB
	CreateOrderFunctionHandler createOrderBean;

	@EJB
	DeleteOrderFunctionHandler deleteOrderBean;

	@EJB
	GetOrderStatusFunctionHandler getStatusBean;

	@EJB
	GetOrderResultsFunctionHandler getResultsBean;

	protected Line psaLine;

	public static final String SERVER_VERSION = "1.0.0_RC1";
	public static final String SERVER_NAME = "PCE_RFC_SERVICE_D";

	private boolean isConnected = false;

	private transient JCoServer jCoServer;
	private transient ServerDataProvider sdp;
	private transient DestinationDataProvider ddp;

	public JCoStartupBean() {
	}

	@PostConstruct
	public void init() {

		try {

			psaLine = lineService.findLineByName("PSA");
			if (psaLine == null) {
				psaLine = lineService.createLine("PSA", "Pilot Server Application");
			}
			logger.setLine(psaLine);
			logger.info("Startup RFC Server " + SERVER_NAME + " v" + SERVER_VERSION);

			try {
				connect();
			} catch (JCoException e) {
				logger.error("Unable to create PCE RFC Server " + SERVER_NAME + " v" + SERVER_VERSION + ", because of " + e.getMessage(), e);
				return;
			}
			logger.info("PCE RFC Server " + SERVER_NAME + " v" + SERVER_VERSION + " started successfully");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@PreDestroy
	public void destroy() {
		Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).info("----------------destroy----------------------");
		try {
			shutdown();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@Override
	public String getServerVersion() {
		return SERVER_VERSION;
	}

	@Override
	public String getServerName() {
		return SERVER_NAME;
	}

	@Override
	public EntityManager getEntityManager() {
		return em;
	}

	public synchronized boolean connect() throws JCoException {
		if (isConnected) {
			return true;
		}
		try {
			if (!Environment.isDestinationDataProviderRegistered()) {
				ddp = new PceDestinationDataProvider();
				Environment.registerDestinationDataProvider(ddp);
			}
			if (!Environment.isServerDataProviderRegistered()) {
				sdp = new PceServerDataProvider();
				Environment.registerServerDataProvider(sdp);
			}

			jCoServer = JCoServerFactory.getServer(SERVER_NAME);

		} catch (Exception ex) {
			ex.printStackTrace();

			if (Environment.isServerDataProviderRegistered() && sdp != null) {
				Environment.unregisterServerDataProvider(sdp);
			}
			if (Environment.isDestinationDataProviderRegistered() && ddp != null) {
				Environment.unregisterDestinationDataProvider(ddp);
			}
			throw new JCoException(0, "", ex.getMessage());
		}

		DefaultServerHandlerFactory.FunctionHandlerFactory factory = new DefaultServerHandlerFactory.FunctionHandlerFactory();
		// factory.registerGenericHandler(new DefaultFunctionHandler());

		factory.registerHandler("Z_PPFM_PCE_ORDER_CREATE", createOrderBean);
		factory.registerHandler("Z_PPFM_PCE_ORDER_DELETE", deleteOrderBean);
		factory.registerHandler("Z_PPFM_PCE_ORDER_RESULTS_GET", getResultsBean);
		factory.registerHandler("Z_PPFM_PCE_ORDER_STATUS_GET", getStatusBean);

		jCoServer.setCallHandlerFactory(factory);

		PceTIDHandler pceTIDHandler = new PceTIDHandler();
		jCoServer.setTIDHandler(pceTIDHandler);

		PceRfcErrorListener eListener = new PceRfcErrorListener();
		jCoServer.addServerErrorListener(eListener);
		jCoServer.addServerExceptionListener(eListener);

		jCoServer.start();
		isConnected = true;

		jCoServer.addServerStateChangedListener(this);

		return true;
	}

	public synchronized void shutdown() {
		if (isConnected && jCoServer != null) {
			jCoServer.release();
			jCoServer.stop();
		}
		if (Environment.isServerDataProviderRegistered() && sdp != null) {
			Environment.unregisterServerDataProvider(sdp);
			sdp = null;
		}
		if (Environment.isDestinationDataProviderRegistered() && ddp != null) {
			Environment.unregisterDestinationDataProvider(ddp);
			ddp = null;
		}

		jCoServer = null;
		isConnected = false;
	}

	public String getStatus() {
		if (jCoServer != null) {
			JCoServerState state = jCoServer.getState();
			return state.name();
		}
		return JCoServerState.DEAD.name();
	}

	public JCoServerState getState() {
		if (jCoServer != null) {
			return jCoServer.getState();
		}
		return JCoServerState.DEAD;
	}

	public String getJCoVersion() {
		String version = null;
		JCoRuntime rt = JCoRuntimeFactory.getRuntime();
		version = rt.getVersion();
		return version;
	}

	public String getJCoAbout() {
		AboutDialog dia = new AboutDialog(SERVER_NAME);

		ByteArrayOutputStream os = null;
		PrintWriter pw = null;
		String about = "";
		try {
			os = new ByteArrayOutputStream();
			pw = new PrintWriter(os);
			dia.printTo(pw);
			pw.flush();
			about = os.toString();
			Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).info(about);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {

			if (pw != null) {
				pw.close();
			}
			if (os != null) {
				try {
					os.close();
				} catch (Exception e) {
				}
			}

		}

		return about;
	}

	public Map<String, String> getJavaRuntimeMap() {
		Map<String, String> map = new HashMap<String, String>();
		String os_version = System.getProperty("os.name") + " " + System.getProperty("os.version") + " for " + System.getProperty("os.arch");
		String java_version = System.getProperty("java.version") + " " + System.getProperty("java.vendor");
		String java_codepage = new InputStreamReader(new ByteArrayInputStream(new byte[0])).getEncoding();

		map.put("Operating System", os_version);
		map.put("Java VM", java_version);
		map.put("Java Codepage", java_codepage);
		return map;
	}

	@Override
	public void serverStateChangeOccurred(JCoServer paramJCoServer, JCoServerState oldState, JCoServerState newState) {
		FacesMessage fm;
		if (newState == JCoServerState.STARTED) {
			fm = new FacesMessage(FacesMessage.SEVERITY_INFO, "STARTED", "RFC Server " + SERVER_NAME + " is started (" + new Date() + ")");
		} else if (newState == JCoServerState.STOPPED) {
			fm = new FacesMessage(FacesMessage.SEVERITY_WARN, "STOPPED", "RFC Server " + SERVER_NAME + " is stopped (" + new Date() + ")");
		} else if (newState == JCoServerState.STOPPING) {
			fm = new FacesMessage(FacesMessage.SEVERITY_WARN, "STOPPING", "RFC Server " + SERVER_NAME + " is stopping (" + new Date() + ")");
		} else if (newState == JCoServerState.ALIVE) {
			fm = new FacesMessage(FacesMessage.SEVERITY_INFO, "ALIVE", "RFC Server " + SERVER_NAME + " is running (" + new Date() + ")");
		} else {
			fm = new FacesMessage(FacesMessage.SEVERITY_FATAL, "DEAD", "RFC Server " + SERVER_NAME + " can not connect to repository (" + new Date() + ")");
		}
		logger.info("RFC Server " + SERVER_NAME + " state changed from " + oldState.name() + " to " + newState.name());
		FacesContext.getCurrentInstance().addMessage(null, fm);

	}

}
